/**************************************************************************/
/*!
  @file     CST816T.cpp
  Author: Atsushi Sasaki (https://github.com/aselectroworks)
  License: MIT (see LICENSE)
*/
/**************************************************************************/

#include "CST816T.h"
#include <Wire.h>

#define REG_GESTURE_ID 0x01
#define REG_FINGER_NUM 0x02
#define REG_XPOS_H 0x03
#define REG_XPOS_L 0x04
#define REG_YPOS_H 0x05
#define REG_YPOS_L 0x06
#define REG_CHIP_ID 0xA7
#define REG_PROJ_ID 0xA8
#define REG_FW_VERSION 0xA9
#define REG_FACTORY_ID 0xAA
#define REG_SLEEP_MODE 0xE5
#define REG_IRQ_CTL 0xFA
#define REG_LONG_PRESS_TICK 0xEB
#define REG_MOTION_MASK 0xEC
#define REG_DIS_AUTOSLEEP 0xFE

#define MOTION_MASK_CONTINUOUS_LEFT_RIGHT 0b100
#define MOTION_MASK_CONTINUOUS_UP_DOWN 0b010
#define MOTION_MASK_DOUBLE_CLICK 0b001

#define IRQ_EN_TOUCH 0x40
#define IRQ_EN_CHANGE 0x20
#define IRQ_EN_MOTION 0x10
#define IRQ_EN_LONGPRESS 0x01

static bool tp_event = false;
static void tp_isr()
{
    tp_event = true;
    // Serial0.println("tp_isr");
}

CST816T::CST816T()
{
    chip_id = 0;
    tp_event = false;
}

CST816T::CST816T(uint8_t rst_n_pin, uint8_t int_n_pin)
{
    rst_n = rst_n_pin;
    int_n = int_n_pin;
    CST816T();
}

CST816T::CST816T(uint8_t sda_pin, uint8_t scl_pin, uint8_t rst_n_pin, uint8_t int_n_pin)
{
    sda = sda_pin;
    scl = scl_pin;
    rst_n = rst_n_pin;
    int_n = int_n_pin;
    CST816T();
}

CST816T::~CST816T()
{
}

void CST816T::begin(void)
{
    // Initialize I2C
    if (sda != -1 && scl != -1)
    {
        Wire.begin(sda, scl);
        Serial.printf("I2C initialized (SDA %d, SCL %d)0", sda, scl);
    }
    else
    {
        Wire.begin();
        Serial.println("I2C initialized (SDA, SCL)1");
    }

    // Int Pin Configuration
    if (int_n != -1)
    {
        pinMode(int_n, INPUT);
    }
    // Reset Pin Configuration
    if (rst_n != -1)
    {
        pinMode(rst_n, OUTPUT);
        digitalWrite(rst_n, LOW);
        delay(10);
        digitalWrite(rst_n, HIGH);
        delay(500);
    }
    // Initialize Touch
    readByte(0x15);
    delay(1);
    chip_id = readByte(0xa7);
    delay(1);
    writeByte(0xEC, 0b00000101);
    delay(1);
    writeByte(0xFA, 0b01110000);
    delay(1);
}
void CST816T::begin(touchpad_mode tp_mode, bool int_enable)
{
    begin();

    if (int_enable)
    {
        uint8_t irq_en = 0x0;
        uint8_t motion_mask = 0x0;
        switch (tp_mode)
        {
        case mode_touch:
            irq_en = IRQ_EN_TOUCH;
            break;
        case mode_change:
            irq_en = IRQ_EN_CHANGE;
            break;
        case mode_fast:
            irq_en = IRQ_EN_MOTION;
            break;
        case mode_motion:
            irq_en = IRQ_EN_MOTION | IRQ_EN_LONGPRESS;
            motion_mask = MOTION_MASK_DOUBLE_CLICK;
            break;
        default:
            break;
        }
        writeByte(REG_IRQ_CTL, irq_en); // 0xFA
        writeByte(REG_MOTION_MASK, motion_mask);

        attachInterrupt(digitalPinToInterrupt(int_n), tp_isr, FALLING);
    }

    // disable auto sleep
    uint8_t dis_auto_sleep = 0xFF;
    writeByte(REG_DIS_AUTOSLEEP, dis_auto_sleep);
}

/**
 * @设置旋转方向,默认为Rotation_0
 * @Rotation：方向 0~3
 */
void CST816T::setRotation(TouchRotation Rotation)
{
    switch (Rotation)
    {
    case Rotation_0:
        touch_rotation = Rotation_0;
        break;
    case Rotation_1:
        touch_rotation = Rotation_1;
        break;
    case Rotation_2:
        touch_rotation = Rotation_2;
        break;
    case Rotation_3:
        touch_rotation = Rotation_3;
        break;
    }
}

// coordinate diagram（FPC downwards）
TouchInfos CST816T::GetTouchInfo(void)
{
    // Serial.println("GetTouchInfo");
    byte error;
    TouchInfos info;
    uint8_t touchData[7];
    uint8_t rdDataCount;
    // 唤醒
    // readByte(0x15);
    // readByte(0xa7);
    if (tp_event)
    {
        tp_event = false;

        uint8_t i = 0;
        long startTime = millis();
        do
        {
            Wire.beginTransmission(I2C_ADDR_CST816T);
            Wire.write(0);                       //
            error = Wire.endTransmission(false); // Restart
            if (error != 0)
            {
                info.isValid = false;
                return info;
            }
            rdDataCount = Wire.requestFrom(I2C_ADDR_CST816T, sizeof(touchData));
            // Serial.printf("读取中%d  %d\n", i, rdDataCount);
            if (millis() - startTime > 1)
            {
                info.isValid = false;
                return info;
            }
        } while (rdDataCount == 0);

        i = 0;

        while (Wire.available())
        {
            touchData[i] = Wire.read();
            // Serial.printf("%d %02X \n", i, touchData[i]);
            i++;
            if (i >= sizeof(touchData))
            {
                break;
            }
        }
        // Serial.println("GetTouchInfo end");

        // This can only be 0 or 1
        uint8_t nbTouchPoints = touchData[touchPointNumIndex] & 0x0f;
        uint8_t xHigh = touchData[touchXHighIndex] & 0x0f;
        uint8_t xLow = touchData[touchXLowIndex];
        uint16_t x = (xHigh << 8) | xLow;
        uint8_t yHigh = touchData[touchYHighIndex] & 0x0f;
        uint8_t yLow = touchData[touchYLowIndex];
        uint16_t y = (yHigh << 8) | yLow;
        Gestures gesture = static_cast<Gestures>(touchData[gestureIndex]);

        // Validity check 无效数据：超范围，手势识别失败
        if (x >= TouchWidth || y >= TouchHeight ||
            (gesture != Gestures::None &&
             gesture != Gestures::SlideDown &&
             gesture != Gestures::SlideUp &&
             gesture != Gestures::SlideLeft &&
             gesture != Gestures::SlideRight &&
             gesture != Gestures::SingleTap &&
             gesture != Gestures::DoubleTap &&
             gesture != Gestures::LongPress))
        {
            info.isValid = false;
            return info;
        }
        info.x = x;
        info.y = y;
        info.touching = (nbTouchPoints > 0);
        info.gesture = gesture;
        info.isValid = true;

        return info;
    }
    // touchPoint.touch_count = read_td_status();

    // touchPoint.tp[id1].status = (touchPoint.tp[id1].status == release) ? touch : stream;
    // switch (touch_rotation) {
    //     case Rotation_0://不变
    //         touchPoint.tp[id1].x = read_touch1_x();
    //         touchPoint.tp[id1].y = read_touch1_y();
    //     break;
    //     case Rotation_1:
    //         touchPoint.tp[id1].x = read_touch1_y();
    //         touchPoint.tp[id1].y = TouchWidth - read_touch1_x();
    //     break;
    //     case Rotation_2:
    //         touchPoint.tp[id1].x = TouchWidth - read_touch1_x();
    //         touchPoint.tp[id1].y = TouchHeight - read_touch1_y();
    //     break;
    //     case Rotation_3:
    //         touchPoint.tp[id1].x = TouchHeight - read_touch1_y();
    //         touchPoint.tp[id1].y = read_touch1_x();
    //     break;
    // }
    // touchPoint.tp[~id1 & 0x01].status = release;

    // return touchPoint;
    return info;
}

String CST816T::state(TouchInfos _info)
{
    String s;
    switch (_info.gesture)
    {
    case Gestures::None:
        s = "NONE";
        break;
    case Gestures::SlideDown:
        s = "SWIPE DOWN";
        break;
    case Gestures::SlideUp:
        s = "SWIPE UP";
        break;
    case Gestures::SlideLeft:
        s = "SWIPE LEFT";
        break;
    case Gestures::SlideRight:
        s = "SWIPE RIGHT";
        break;
    case Gestures::SingleTap:
        s = "SINGLE CLICK";
        break;
    case Gestures::DoubleTap:
        s = "DOUBLE CLICK";
        break;
    case Gestures::LongPress:
        s = "LONG PRESS";
        break;
    default:
        s = "UNKNOWN 0x";
        s += String(_info.gesture, HEX);
        break;
    }
    s += '\t' + String(_info.x) + '\t' + String(_info.y);
    return s;
}

// Private Function
uint8_t CST816T::readByte(uint8_t addr)
{
    uint8_t rdData;
    uint8_t rdDataCount;
    do
    {
        Wire.beginTransmission(I2C_ADDR_CST816T);
        Wire.write(addr);
        Wire.endTransmission(false); // Restart
        rdDataCount = Wire.requestFrom(I2C_ADDR_CST816T, 1);
    } while (rdDataCount == 0);
    while (Wire.available())
    {
        rdData = Wire.read();
    }
    return rdData;
}

void CST816T::writeByte(uint8_t addr, uint8_t data)
{
    Wire.beginTransmission(I2C_ADDR_CST816T);
    Wire.write(addr);
    Wire.write(data);
    Wire.endTransmission();
}
